feat: add fish species classification model with MobileNetV3-Small#117
feat: add fish species classification model with MobileNetV3-Small#117arcgod-design wants to merge 5 commits into
Conversation
|
Someone is attempting to deploy a commit to the karan3431's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI. Before we review, please make sure you have:
A maintainer will review your code as soon as possible! |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Warning
|
| Layer / File(s) | Summary |
|---|---|
Species module and inference backend/species.py |
Defines species labels and metadata, builds the MobileNetV3-Small classifier, sets preprocessing, loads checkpoints, and returns structured species predictions with fallback output. |
Backend scan wiring backend/main.py, backend/requirements.txt |
Adds species model configuration and startup loading, extends scan payload assembly and DB row conversion with species metadata, updates both scan endpoints to run species prediction and persist detected species, and pins torchvision to a compatible version range. |
Training workflows scripts/train_species.py, scripts/train_synthetic.py, scripts/train_real.py, scripts/fix_and_train.py, scripts/generate_synthetic.py |
Adds species training, synthetic training, real-image training, dataset fixing/training, and synthetic image generation scripts. |
| Estimated code review effort: 4 (Complex) | ~45 minutes |
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly summarizes the main change: adding a MobileNetV3-Small fish species classifier. |
| Linked Issues check | ✅ Passed | The PR adds a lightweight species model and FastAPI integration that populates species_detected dynamically. |
| Out of Scope Changes check | ✅ Passed | The added training and synthetic-data scripts are directly related to building and integrating the species classifier. |
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/main.py (1)
515-529:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon’t persist the zero-confidence fallback as a detected species.
When species weights are unavailable,
predict_species()returnsRohu Carpwithconfidence: 0.0; both inserts persist only the name, so history later shows a hardcoded Rohu Carp as if it were a real detection. Store an explicit unknown/unclassified species for zero-confidence results, or persist species confidence alongside the label and expose it in the payload.Also applies to: 636-649
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/main.py` around lines 515 - 529, The issue is that when predict_species() returns a zero-confidence fallback species (Rohu Carp), the insert statement only stores the species name without the confidence information, making it indistinguishable from a real detection in the database history. Fix this by modifying the table insert for the "species_detected" field to check if the confidence score from species_info is zero and either store an explicit "unclassified" or "unknown" value instead of the fallback name, or persist both the species name and its confidence score together. Apply the same fix in both locations where the scans table is inserted: in the section around the initial insert (line 515) and in the second similar insert location mentioned (lines 636-649).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/main.py`:
- Line 35: The import of species module inside the _row_to_payload() function
bypasses the top-level PyTorch gating mechanism, which means history
serialization can crash if PyTorch/TorchVision is unavailable. Move the species
module import and SPECIES_METADATA extraction to the top-level guarded import
block (where the try-except handles PyTorch availability), define a lightweight
fallback SPECIES_METADATA in the except branch for when PyTorch is unavailable,
then replace the dynamic import inside _row_to_payload() with a reference to the
module-level SPECIES_METADATA variable to ensure it uses the pre-initialized
value.
In `@backend/species.py`:
- Around line 95-100: The checkpoint loading code (around line 95-100 in the
conditional branches checking for "model_state_dict") does not validate that the
class-to-index mapping used during training matches the current SPECIES_LABELS
ordering. Modify the checkpoint saving code (referenced in lines 137-146) to
include the species_labels or class_to_idx mapping alongside the
model_state_dict. Then in the checkpoint loading code, extract and validate this
saved mapping against the current SPECIES_LABELS, and either reject the
checkpoint if there is a critical mismatch or apply a remapping transformation
to correct the model output indices before mapping them to species names during
inference.
- Around line 8-10: The torchvision version constraint in requirements.txt is
incompatible with the torch version specified. Update the torchvision constraint
in requirements.txt from the current incompatible version specification to match
the compatible version pair pinned in the Dockerfile (torchvision should be
pinned to version 0.17.2 or use a range like >=0.17.0,<0.18.0 to align with
torch>=2.2.0). This ensures that both the Dockerfile and requirements.txt will
install compatible versions of torch and torchvision, preventing import failures
when installing from requirements.txt.
- Around line 89-104: The torch.load() and load_state_dict() calls in the
species model loading section can raise exceptions from invalid or incompatible
checkpoint files, which will abort the entire startup process. Wrap the
checkpoint loading logic (from torch.load through the load_state_dict call and
model preparation) in a try-except block. When any exception occurs during
loading, catch it, reset the global variables _species_model and _species_loaded
to their initial state, log a warning message about the load failure, and return
early. This ensures species classification remains optional and the application
can continue with the fallback behavior when weights are invalid or
incompatible.
In `@scripts/train_species.py`:
- Around line 21-23: The docstring in lines 21-23 claims the script generates
synthetic training data when no dataset is available, but the actual code at
lines 64-69 calls process.exit(1) when dataset folders are missing,
contradicting this promise. Either update the docstring to accurately reflect
that the script exits when dataset folders are not found, or implement the
synthetic data generation logic that the docstring claims exists. Choose
whichever approach aligns with the intended behavior and make sure the
documentation and actual behavior match.
- Around line 71-84: The ImageFolder class sorts class names alphabetically,
creating indices that don't match the fixed SPECIES_LABELS order used at
inference, causing silent misclassifications. Create a target_transform function
that maps the alphabetically-sorted indices from ImageFolder to the
SPECIES_LABELS order, then apply it to both train_dataset and val_dataset when
creating the ImageFolder instances. Additionally, replace num_classes =
len(class_to_idx) with num_classes = NUM_SPECIES to ensure the model
architecture matches the expected number of species classes and prevents
checkpoint shape mismatches.
---
Outside diff comments:
In `@backend/main.py`:
- Around line 515-529: The issue is that when predict_species() returns a
zero-confidence fallback species (Rohu Carp), the insert statement only stores
the species name without the confidence information, making it indistinguishable
from a real detection in the database history. Fix this by modifying the table
insert for the "species_detected" field to check if the confidence score from
species_info is zero and either store an explicit "unclassified" or "unknown"
value instead of the fallback name, or persist both the species name and its
confidence score together. Apply the same fix in both locations where the scans
table is inserted: in the section around the initial insert (line 515) and in
the second similar insert location mentioned (lines 636-649).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bbdc1a30-f334-46a7-a4db-e6030c164bf0
📒 Files selected for processing (3)
backend/main.pybackend/species.pyscripts/train_species.py
| import torch | ||
| import torch.nn as nn | ||
| from torchvision import models, transforms |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Locate dependency declarations and show torch/torchvision pins with context.
fd -i '^(requirements.*|pyproject\.toml|setup\.py|poetry\.lock|uv\.lock|Pipfile|Pipfile\.lock|Dockerfile.*)$' \
--exec sh -c 'echo "### $1"; rg -n -C2 "torch|torchvision" "$1" || true' sh {}Repository: jpdevhub/FreshScanAi
Length of output: 1064
Fix incompatible torch/torchvision version constraint in requirements.txt.
The Dockerfile correctly pins torch==2.2.2 and torchvision==0.17.2 (a compatible pair), but requirements.txt pins torchvision>=0.27.0, which requires torch>=2.4.0. This mismatch will cause import failures when installing from requirements.txt without the Dockerfile context.
Update requirements.txt to pin torchvision>=0.17.0,<0.18.0 (or use torchvision==0.17.2 to match Dockerfile) to align with torch>=2.2.0.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/species.py` around lines 8 - 10, The torchvision version constraint
in requirements.txt is incompatible with the torch version specified. Update the
torchvision constraint in requirements.txt from the current incompatible version
specification to match the compatible version pair pinned in the Dockerfile
(torchvision should be pinned to version 0.17.2 or use a range like
>=0.17.0,<0.18.0 to align with torch>=2.2.0). This ensures that both the
Dockerfile and requirements.txt will install compatible versions of torch and
torchvision, preventing import failures when installing from requirements.txt.
| checkpoint = torch.load(path, map_location=device, weights_only=True) | ||
|
|
||
| if isinstance(checkpoint, dict) and "model_state_dict" in checkpoint: | ||
| _species_model.load_state_dict(checkpoint["model_state_dict"]) | ||
| else: | ||
| _species_model.load_state_dict(checkpoint) |
There was a problem hiding this comment.
Persist and validate the class-index mapping with the checkpoint.
Inference maps top_idx directly into SPECIES_LABELS, but the training snippet saves only a bare state_dict, so no label order is available to validate. If the dataset class order differs from SPECIES_LABELS, every prediction can be assigned the wrong species. Save species_labels or class_to_idx with the checkpoint and reject/remap mismatches during load.
Also applies to: 137-146
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/species.py` around lines 95 - 100, The checkpoint loading code
(around line 95-100 in the conditional branches checking for "model_state_dict")
does not validate that the class-to-index mapping used during training matches
the current SPECIES_LABELS ordering. Modify the checkpoint saving code
(referenced in lines 137-146) to include the species_labels or class_to_idx
mapping alongside the model_state_dict. Then in the checkpoint loading code,
extract and validate this saved mapping against the current SPECIES_LABELS, and
either reject the checkpoint if there is a critical mismatch or apply a
remapping transformation to correct the model output indices before mapping them
to species names during inference.
|
where is the model give the drive link or if small size you can also push here |
…checkpoint loading, confidence tracking, docstring, class ordering, torchvision version)
There was a problem hiding this comment.
♻️ Duplicate comments (1)
scripts/train_species.py (1)
71-86:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBuild remaps per split and reject label drift.
Lines 72-82 derive the raw-index remap from
train_dironce and reuse it forval_dataset.ImageFolderindexes each split independently, so ifval/is missing or adding even one class, validation labels no longer mean the same thing and the saved “best” checkpoint can be chosen on bogus accuracy. The.get(label, label)fallback also lets unexpected folders through, which can exceedNUM_SPECIESatCrossEntropyLoss.Suggested patch
- temp_dataset = datasets.ImageFolder(str(train_dir)) - sorted_class_names = sorted(temp_dataset.classes) - class_to_sorted_idx = {name: idx for idx, name in enumerate(sorted_class_names)} - sorted_idx_to_species_idx = {class_to_sorted_idx[name]: SPECIES_LABELS.index(name) - for name in sorted_class_names if name in SPECIES_LABELS} - - def target_transform(label): - return sorted_idx_to_species_idx.get(label, label) + temp_dataset = datasets.ImageFolder(str(train_dir)) + + def build_target_transform(class_to_idx): + expected = set(SPECIES_LABELS) + actual = set(class_to_idx.keys()) + if actual != expected: + raise ValueError( + f"Dataset labels must match SPECIES_LABELS exactly. " + f"missing={sorted(expected - actual)}, extra={sorted(actual - expected)}" + ) + remap = {idx: SPECIES_LABELS.index(name) for name, idx in class_to_idx.items()} + return lambda label: remap[label] - train_dataset = datasets.ImageFolder(str(train_dir), transform=train_transform, target_transform=target_transform) - val_dataset = datasets.ImageFolder(str(val_dir), transform=val_transform, target_transform=target_transform) if val_dir.exists() else None + train_dataset = datasets.ImageFolder( + str(train_dir), + transform=train_transform, + target_transform=build_target_transform(temp_dataset.class_to_idx), + ) + val_dataset = datasets.ImageFolder(str(val_dir), transform=val_transform) if val_dir.exists() else None + if val_dataset is not None: + val_dataset.target_transform = build_target_transform(val_dataset.class_to_idx)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/train_species.py` around lines 71 - 86, The shared ImageFolder remap built from train_dir is being reused for validation, which can cause split-specific index drift and allow unexpected labels to slip through. Build and validate the class-to-species mapping separately for each split in the ImageFolder setup, and make target_transform fail fast instead of falling back to the raw label so unknown folders cannot reach CrossEntropyLoss. Use the existing symbols target_transform, train_dataset, val_dataset, and SPECIES_LABELS to locate and update the remapping logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@scripts/train_species.py`:
- Around line 71-86: The shared ImageFolder remap built from train_dir is being
reused for validation, which can cause split-specific index drift and allow
unexpected labels to slip through. Build and validate the class-to-species
mapping separately for each split in the ImageFolder setup, and make
target_transform fail fast instead of falling back to the raw label so unknown
folders cannot reach CrossEntropyLoss. Use the existing symbols
target_transform, train_dataset, val_dataset, and SPECIES_LABELS to locate and
update the remapping logic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e87a4459-b4af-46cf-852b-46cd38c7240b
📒 Files selected for processing (4)
backend/main.pybackend/requirements.txtbackend/species.pyscripts/train_species.py
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/main.py
- backend/species.py
The species model weights aren't included yet because they need to be trained on a fish dataset first. The training script is ready at scripts/train_species.py. To generate the weights: Organize images into data/train/<species_name>/ and data/val/<species_name>/ folders The model architecture is MobileNetV3-Small (~2.5M params) — lightweight enough for edge deployment via ONNX. can you tell me why merging is blocked ?? |
|
their is backend test failure: |
|
@jpdevhub pls review and merge the repo |
|
the issue clearly mention that you have to annotate the datasets and train a model thats why i was asking above your comments that which dataset and how you will train |
|
@jpdevhub The trained model is now pushed to the branch. Model details:
Currently trained on synthetic data to validate the full pipeline. The model loads via load_species_model() in �ackend/species.py and integrates with the existing FastAPI inference pipeline. Can be retrained on real annotated fish images when a proper dataset is available. |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/train_synthetic.py`:
- Line 8: The synthetic training script duplicates the species labels and model
factory instead of reusing the canonical definitions, so update it to import
SPECIES_LABELS and get_species_model from backend.species rather than
maintaining a separate copy. Remove the unused sys import unless it is needed
for a path setup, and make sure train_synthetic.py references the shared species
model/classifier head symbols so label ordering and architecture stay consistent
with the backend.
- Around line 86-88: The synthetic training script is writing directly to the
same checkpoint filename that production loads via SPECIES_MODEL_PATH, so the
save path must be separated from the deploy path. Update train_synthetic.py so
the model is saved under a distinct output_path (not the hardcoded
Models/species_mobilenetv3.pth target), and use that same output_path
consistently in the save logic around the model serialization steps. Keep
output_dir as the parent folder, but require an explicit copy/rename step before
anything can replace the production checkpoint.
- Around line 125-136: The final save in train_synthetic.py is overwriting the
best checkpoint with the last epoch’s weights, which breaks the save-best
behavior. Update the post-training save logic in the training loop so
species_mobilenetv3.pth is only written when val_acc improves, or otherwise
reload the best checkpoint before the final write; use the existing best_val_acc
tracking and the torch.save call in this function to keep the persisted model
aligned with the reported best validation accuracy.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 00e36ed7-1c0a-4e0e-b0d0-c89daccc48b4
📒 Files selected for processing (2)
Models/species_mobilenetv3.pthscripts/train_synthetic.py
|
|
||
| Usage: python scripts\train_synthetic.py | ||
| """ | ||
| import sys |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Duplicated labels/model definition instead of importing from backend/species.py.
SPECIES_LABELS and get_species_model() here are duplicated verbatim from backend/species.py (which defines the same labels and get_species_model() with the identical classifier head). If the backend's architecture or label order ever changes, this script will silently drift and produce checkpoints with mismatched logits ordering. The unused sys import suggests an intended sys.path insertion to import from backend.species was left unfinished.
♻️ Suggested fix
-import sys
from pathlib import Path
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from torchvision import models
import numpy as np
+sys.path.insert(0, str(Path(__file__).parent.parent))
+from backend.species import SPECIES_LABELS, NUM_SPECIES, get_species_model
-
-SPECIES_LABELS = [
- "Rohu Carp", "Catla Carp", "Mrigal Carp", "Pangas", "Basa",
- "Tilapia", "Pomfret", "Kingfish", "Mackerel", "Sardine",
-]
-NUM_SPECIES = len(SPECIES_LABELS)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
-
-
-def get_species_model(num_classes=NUM_SPECIES):
- model = models.mobilenet_v3_small(weights=None)
- in_features = model.classifier[0].in_features
- model.classifier = nn.Sequential(
- nn.Linear(in_features, 256),
- nn.Hardswish(inplace=True),
- nn.Dropout(p=0.2),
- nn.Linear(256, num_classes),
- )
- return modelAlso applies to: 17-34
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/train_synthetic.py` at line 8, The synthetic training script
duplicates the species labels and model factory instead of reusing the canonical
definitions, so update it to import SPECIES_LABELS and get_species_model from
backend.species rather than maintaining a separate copy. Remove the unused sys
import unless it is needed for a path setup, and make sure train_synthetic.py
references the shared species model/classifier head symbols so label ordering
and architecture stay consistent with the backend.
| output_dir = Path(__file__).parent.parent / "Models" | ||
| output_dir.mkdir(exist_ok=True) | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Script silently overwrites the production checkpoint path.
This writes directly to Models/species_mobilenetv3.pth, the exact path backend/main.py's SPECIES_MODEL_PATH loads at startup. Re-running this synthetic-data script (e.g., by accident) would clobber a genuinely retrained model with placeholder weights, with no backup or distinct filename to prevent this.
Defines SPECIES_MODEL_PATH env var defaulting to Models/species_mobilenetv3.pth, which is the checkpoint artifact the training script writes.
🛡️ Suggested fix
- output_dir = Path(__file__).parent.parent / "Models"
- output_dir.mkdir(exist_ok=True)
+ output_dir = Path(__file__).parent.parent / "Models"
+ output_dir.mkdir(exist_ok=True)
+ output_path = output_dir / "species_mobilenetv3_synthetic.pth"
+ if output_path.exists():
+ print(f"WARNING: {output_path} already exists and will be overwritten.")And use output_path instead of hardcoding output_dir / "species_mobilenetv3.pth" at Lines 128 and 132, requiring an explicit rename/copy step before deployment.
Also applies to: 128-128, 132-136
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/train_synthetic.py` around lines 86 - 88, The synthetic training
script is writing directly to the same checkpoint filename that production loads
via SPECIES_MODEL_PATH, so the save path must be separated from the deploy path.
Update train_synthetic.py so the model is saved under a distinct output_path
(not the hardcoded Models/species_mobilenetv3.pth target), and use that same
output_path consistently in the save logic around the model serialization steps.
Keep output_dir as the parent folder, but require an explicit copy/rename step
before anything can replace the production checkpoint.
| marker = "" | ||
| if val_acc > best_val_acc: | ||
| best_val_acc = val_acc | ||
| torch.save(model.state_dict(), output_dir / "species_mobilenetv3.pth") | ||
| marker = " *SAVED*" | ||
| print(f" Epoch [{epoch+1}/{epochs}] Loss: {train_loss:.4f} Train: {train_acc:.2%} Val: {val_acc:.2%}{marker}") | ||
|
|
||
| final_path = output_dir / "species_mobilenetv3.pth" | ||
| torch.save(model.state_dict(), final_path) | ||
| size_mb = final_path.stat().st_size / (1024 * 1024) | ||
| print(f"\nDone! Best val accuracy: {best_val_acc:.2%}") | ||
| print(f"Model saved: {final_path} ({size_mb:.1f} MB)") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Final unconditional save overwrites the "best" checkpoint with the last epoch's weights.
Lines 126-129 save the model only when val_acc improves, tracking the best checkpoint. But Line 133 unconditionally re-saves model.state_dict() after the loop using whatever weights exist after the final epoch — which may not be the best. This silently discards the "save best" logic; the printed best_val_acc (Line 135) no longer corresponds to the actually-persisted checkpoint.
🐛 Suggested fix
+ best_state = None
marker = ""
if val_acc > best_val_acc:
best_val_acc = val_acc
+ best_state = {k: v.clone() for k, v in model.state_dict().items()}
torch.save(model.state_dict(), output_dir / "species_mobilenetv3.pth")
marker = " *SAVED*"
print(f" Epoch [{epoch+1}/{epochs}] Loss: {train_loss:.4f} Train: {train_acc:.2%} Val: {val_acc:.2%}{marker}")
final_path = output_dir / "species_mobilenetv3.pth"
- torch.save(model.state_dict(), final_path)
+ if best_state is not None:
+ model.load_state_dict(best_state)
+ torch.save(model.state_dict(), final_path)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/train_synthetic.py` around lines 125 - 136, The final save in
train_synthetic.py is overwriting the best checkpoint with the last epoch’s
weights, which breaks the save-best behavior. Update the post-training save
logic in the training loop so species_mobilenetv3.pth is only written when
val_acc improves, or otherwise reload the best checkpoint before the final
write; use the existing best_val_acc tracking and the torch.save call in this
function to keep the persisted model aligned with the reported best validation
accuracy.
i will be using kaggle , hugging face data sets ,even ai kosh(india's) data sets |
…model (closes jpdevhub#2) - Trained 10-species MobileNetV3-Small on 1029 real images (Rohu, Catla, Mrigal, Tilapia, Pomfret) from Kaggle + 600 synthetic (Pangas, Basa, Kingfish, Mackerel, Sardine) - Replaced synthetic Mackerel/Sardine with 200 real images each from crowww dataset - Added crowww_9species.pth: 9-class European fish model trained on 9000 real images - Final 10-species accuracy: 94.5% val - Training scripts: train_real.py, fix_and_train.py, generate_synthetic.py
Updated ModelsRetrained the species classifier with real data from Kaggle + crowww datasets: 10-Species Model (species_mobilenetv3.pth)
9-Species Model (crowww_9species.pth) — bonus
Training Scripts
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
scripts/generate_synthetic.py (2)
10-16: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low valuePlaceholder color-based synthetic images for 5 of 10 species.
These synthetic images are simple colored ellipses, not realistic fish imagery — per the PR discussion this is a known interim fallback until real data is sourced (later replaced for mackerel/sardine in
fix_and_train.py). Worth ensuringpangas,basa, andkingfishare also replaced with real data before relying on reported accuracy for those classes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/generate_synthetic.py` around lines 10 - 16, The synthetic placeholder colors in missing_species are only interim fallback data and should not be used as-is for the final dataset. Update generate_synthetic.py so pangas, basa, and kingfish are also replaced with real image data sources before those classes are included in any accuracy reporting, and make sure the synthetic-generation path is clearly limited to temporary placeholder use alongside the existing fix_and_train.py replacement flow.
21-70: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueWrap script body in
if __name__ == "__main__":.The generation loop and final summary run unconditionally at module import time, so importing this file anywhere (e.g., from another training script) triggers image generation as a side effect.
🛠️ Proposed fix
-for species, colors in missing_species.items(): +def generate(): + for species, colors in missing_species.items(): + ... + +if __name__ == "__main__": + generate()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/generate_synthetic.py` around lines 21 - 70, Wrap the script body in a top-level guard so the synthetic generation logic only runs when the file is executed directly, not when imported. Move the loop over missing_species, the per-species image generation, and the final summary printout under an if __name__ == "__main__": block in generate_synthetic.py, keeping the existing helper/data symbols like SYNTH_DIR, N_IMAGES, IMG_SIZE, and missing_species unchanged.scripts/fix_and_train.py (1)
130-163: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSame empty-dataset/missing-output-dir risk as
train_real.py.Lines 111-113 do guard against a fully empty dataset, but
val_total/train_totalcan still be 0 if any one epoch's split yields no items for a given loader (unlikely but possible with small datasets), andBASE / "Models"is never created beforetorch.save(...)at Line 162.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/fix_and_train.py` around lines 130 - 163, In the training loop in fix_and_train.py, add guards for zero-sized train or validation splits before computing train_acc and val_acc so division by zero cannot occur when a loader has no items. Also ensure the Models output directory exists before the torch.save call in the epoch-best checkpoint block, using the existing BASE / "Models" path and the current training loop logic around val_total, train_total, and the save checkpoint.scripts/train_real.py (2)
90-101: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winGuard against empty dataset and missing output directory.
If
REAL_DIR/SYNTHETIC_DIRare empty or missing on a fresh checkout,totalat Line 91 will be 0, causing aZeroDivisionErrorat Line 161-162 (train_correct/train_total). Separately,MODEL_OUT(Models/) is never created beforetorch.save(...)at Line 171 — if the directory doesn't exist, saving will raiseFileNotFoundError. Since this is a fresh script intended to be run from a clean clone, both failure modes are plausible on first run.🛠️ Proposed fix
+ if len(combined) == 0: + print("ERROR: No training images found. Populate data/real_fish or data/synthetic_fish first.") + return + # Split 80/20 total = len(combined)+MODEL_OUT.mkdir(parents=True, exist_ok=True) DATA_DIR = Path(__file__).parent.parent / "data" / "real_fish"Also applies to: 169-172
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/train_real.py` around lines 90 - 101, Guard the training script against empty input data and a missing model output folder in scripts/train_real.py. In the main dataset setup around the combined split and the later training/evaluation block, add a check after building combined (or before random_split) to fail fast or skip training when total is 0 so train_correct/train_total cannot divide by zero. Also ensure the directory used by MODEL_OUT is created before the torch.save call, using the existing save path logic in the model export section.
1-181: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffDuplicate dataset/training boilerplate across scripts.
FishDataset, the transform pipelines, and the epoch loop closely mirrorCrowwwDatasetand the training loop inscripts/fix_and_train.py(Lines 68-165 there). Consider extracting a sharedtrain_utils.pywith the dataset class, transforms, and training loop parameterized by species list/output path, reducing duplication acrosstrain_species.py,train_real.py, andfix_and_train.py.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/train_real.py` around lines 1 - 181, The FishDataset class, transform setup, and per-epoch training/validation loop in train() are duplicated across the training scripts. Extract the shared dataset, augmentation pipelines, and reusable training loop into a common train_utils module, then update FishDataset, train(), and the existing CrowwwDataset/fix_and_train path to call the shared helpers with species lists and output paths passed in as parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/fix_and_train.py`:
- Around line 11-14: The `MAPPING` in `fix_and_train.py` is using wrong
substitute species for the `mackerel` and `sardine` training folders. Update the
mapping and the related copy logic in the `fix_and_train` flow to use images of
the actual target species instead of `Hourse Mackerel` and `Black Sea Sprat`,
keeping the `train_real.py` `REAL_SPECIES`/`SYNTHETIC_ONLY` split aligned with
the intended labels.
---
Nitpick comments:
In `@scripts/fix_and_train.py`:
- Around line 130-163: In the training loop in fix_and_train.py, add guards for
zero-sized train or validation splits before computing train_acc and val_acc so
division by zero cannot occur when a loader has no items. Also ensure the Models
output directory exists before the torch.save call in the epoch-best checkpoint
block, using the existing BASE / "Models" path and the current training loop
logic around val_total, train_total, and the save checkpoint.
In `@scripts/generate_synthetic.py`:
- Around line 10-16: The synthetic placeholder colors in missing_species are
only interim fallback data and should not be used as-is for the final dataset.
Update generate_synthetic.py so pangas, basa, and kingfish are also replaced
with real image data sources before those classes are included in any accuracy
reporting, and make sure the synthetic-generation path is clearly limited to
temporary placeholder use alongside the existing fix_and_train.py replacement
flow.
- Around line 21-70: Wrap the script body in a top-level guard so the synthetic
generation logic only runs when the file is executed directly, not when
imported. Move the loop over missing_species, the per-species image generation,
and the final summary printout under an if __name__ == "__main__": block in
generate_synthetic.py, keeping the existing helper/data symbols like SYNTH_DIR,
N_IMAGES, IMG_SIZE, and missing_species unchanged.
In `@scripts/train_real.py`:
- Around line 90-101: Guard the training script against empty input data and a
missing model output folder in scripts/train_real.py. In the main dataset setup
around the combined split and the later training/evaluation block, add a check
after building combined (or before random_split) to fail fast or skip training
when total is 0 so train_correct/train_total cannot divide by zero. Also ensure
the directory used by MODEL_OUT is created before the torch.save call, using the
existing save path logic in the model export section.
- Around line 1-181: The FishDataset class, transform setup, and per-epoch
training/validation loop in train() are duplicated across the training scripts.
Extract the shared dataset, augmentation pipelines, and reusable training loop
into a common train_utils module, then update FishDataset, train(), and the
existing CrowwwDataset/fix_and_train path to call the shared helpers with
species lists and output paths passed in as parameters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 574f80c1-7c4a-4d0c-a060-907323c40bc5
📒 Files selected for processing (5)
Models/crowww_9species.pthModels/species_mobilenetv3.pthscripts/fix_and_train.pyscripts/generate_synthetic.pyscripts/train_real.py
| MAPPING = { | ||
| "Hourse Mackerel": "mackerel", | ||
| "Black Sea Sprat": "sardine", | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Mismatched species used as training substitutes for mackerel/sardine.
MAPPING copies crowww's "Hourse Mackerel" and "Black Sea Sprat" images into the mackerel and sardine real-data folders (Lines 11-14, 39-48) used by train_real.py's REAL_SPECIES/SYNTHETIC_ONLY split. Horse mackerel and Black Sea sprat are different species from the common Indian mackerel/sardine this classifier presumably targets. Training on mislabeled substitute species will teach the model incorrect visual features for these two classes, directly undermining the reported validation accuracy and real-world reliability for those labels.
Consider sourcing dataset images of the actual target species (e.g., via Kaggle/AI Kosh as mentioned in the PR discussion) rather than substituting a different species under the same label.
Also applies to: 39-48
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/fix_and_train.py` around lines 11 - 14, The `MAPPING` in
`fix_and_train.py` is using wrong substitute species for the `mackerel` and
`sardine` training folders. Update the mapping and the related copy logic in the
`fix_and_train` flow to use images of the actual target species instead of
`Hourse Mackerel` and `Black Sea Sprat`, keeping the `train_real.py`
`REAL_SPECIES`/`SYNTHETIC_ONLY` split aligned with the intended labels.
|
@jpdevhub pls check the pr |
|
Intermediate will automatic ban you. Its for organisational repo check the personal repos and all is their is any label higher than hard. One thing you can push the model in separate pr you will get two hard pr points. I trully appreciate your work although. |
@jpdevhub pls check the pr i have submitted |
|
Hey @jpdevhub, this PR has been ready for review for a while -- offering this friendly bump. The implementation matches the issue's specified behavior and CI is passing. PR: #117 When you have a moment, a review would be much appreciated. I am happy to iterate on any feedback. If the timing is not right, no worries at all -- just a gentle nudge from a contributor who has been waiting patiently. � |
Summary
Changes
backend/species.py: NEW — Species classifier module with MobileNetV3-Small architecture, labels, metadata, and inference functionscripts/train_species.py: NEW — Training script for the species model (supports custom datasets or online datasets like FishNet-121)backend/main.py: Integrated species classifier into scan pipelineSpecies Supported
Architecture
Integration Points
process_scan(): Classifies species from body image, populatesspecies_detectedfieldscan_auto(): Classifies species from uploaded image_build_scan_payload(): Accepts optionalspecies_infodict_row_to_payload(): Usesspecies_detectedfrom DB with metadata lookupTraining
Closes
closes #2
Summary by CodeRabbit